import _ from 'lodash';
import { toJS } from 'mobx';
import React from 'react';
import { Icon } from 'antd';
import defaultData from "./data";
import classNames from 'classnames';
import BaseDesign from "../base/Design";
import Sortable from '@timluo465/react-sortablejs';
import utils from "../../../../../utils";
import { Resizable } from 're-resizable';
import Droppable from '../../../../common/droppable';
import { DragKey, dragDelayTime } from "../../../../../constants/form";
import { parseWidths } from './transfer';
import { smoothWidths } from '..';
import { WeaCheckbox, WeaLocaleProvider } from 'ecCom';
import EE, { actions } from '../../event/component';
import './index.less';
const { getLabel } = WeaLocaleProvider;

const borderWidth = 1;
const draggingBorderWidth = 2;
const resizableMinWidth = 160;
const fixdLeftWidth = 38;

const ActionGroup = ({ onAction, isCopy, isDelete }) => {
  const { Add, Copy, Delete } = ActionGroup.types;
  const _action = type => e => {
    e.stopPropagation();
    onAction(type);
  };
  const copyClassName = classNames('action-copy', {
    'wea-hide': !isCopy
  });
  const deleteClassName = classNames('action-delete', {
    'wea-hide': !isDelete
  })

  return (
    <div class="action-group">
      <span class="action-add" onClick={_action(Add)}><Icon ecId={`${this && this.props && this.props.ecId || ''}_Icon@tyky0u`} type='plus' />{getLabel('511752','添加列')}</span>
      <span className={copyClassName} onClick={_action(Copy)}><Icon ecId={`${this && this.props && this.props.ecId || ''}_Icon@pwi9t6`} type='copy' />{getLabel('511753','复制')}</span>
      <span className={deleteClassName} onClick={_action(Delete)}><i class="icon-coms-delete"></i>{getLabel('511754','删除列')}</span>
    </div> 
  );
};

ActionGroup.types = {
  Add: 'add',
  Copy: 'copy',
  Delete: 'delete'
};

const FixedCol = () => (
  <div className='wea-edc-datatable-fixed-left'>
    <div className='wea-edc-com-title'>
      <WeaCheckbox ecId={`${this && this.props && this.props.ecId || ''}_WeaCheckbox@ykxbif`} />
    </div>
    <div><WeaCheckbox ecId={`${this && this.props && this.props.ecId || ''}_WeaCheckbox@xtn84u`} /></div>
  </div>
);

const droppableOpts = {
  group: {
    name: 'datatable_child',
    pull: false,
    put: [DragKey.ComCard]
  }
};
const sortableOpts = {
  group: {
    name: DragKey.DataTable,
    direction: 'vertical',
  },
  scroll: true,
  scrollSensitivity: 120,
  delay: dragDelayTime,
  chosenClass: 'wea-edc-dragging',
  dragoverBubble: false,
  forceFallback: true,
  swapThreshold: 0.65
};

export default class DataTableDesign extends BaseDesign {
  containerRef = React.createRef();
  resizableRefs = [];

  descs = null;

  state = {
    width: 0,
    resizing: -1
  }

  componentDidMount() {
    const container = this.containerRef.current;
    const baseWidth = container.offsetWidth;
    const width = this.calcWidth();
    
    this.setState({ width });
    this.baseWidth = baseWidth;
  }

  componentWillReceiveProps(nextProps) {
    const { coms } = this.props.data; 
    const { coms: newComs } = nextProps.data;
    const newLength = newComs.filter(Boolean).length;
    if (newComs.length > coms.length || ( newLength > 0 && coms.length == 1 )) {
      let index = -1;

      newComs.every((com, i) => {
        return (index = i + 1) && coms[i] && com.uuid === coms[i].uuid;
      });
      
      this.setState({ 
        width: this.calcWidth()
      });

      setTimeout(() => {
        this.scrollTo(index - 1)
      }, 10);
    }
  }

  componentDidUpdate() {
    const container = this.containerRef.current;

    if (!container) return;

    setTimeout(() => {
      const { style } = container;

      if (this.isDescChange()) {
        this.calcTitleHeight(container);
      }

      container.removeAttribute('style');
      style.height = `${container.offsetHeight}px`;
    });
  }

  calcWidth() {
    let fixedLeftW = 0;
    const { coms } = this.props.data;
    const container = this.containerRef.current;

    if (coms.filter(Boolean).length) {
      fixedLeftW = fixdLeftWidth;
    }

    return container.offsetWidth - 2 * borderWidth - fixedLeftW;
  }

  isDescChange() {
    const { coms } = this.props.data;
    const descs = coms.map(com => !com ? void 0 : com.config.desc);
    const isChange = !_.isEqual(this.descs, descs);

    if (isChange) {
      this.descs = descs;
    }

    return isChange;
  }

  calcTitleHeight(container) {
    const titles = [...container.querySelectorAll('.wea-edc-com-title') || []];
    const heights = titles.map(title => {
      title.removeAttribute('style');
      return title.offsetHeight;
    });
    const titleHeight = Math.max(...heights);
    
    titles.forEach(title => {
      title.style.height = utils.toPixel(titleHeight);
    });
  }

  onDragStart(e) {
    const el = e.item;
    let diff = 2 * draggingBorderWidth;

    if (!el.classList.contains('wea-edc-datatable-col')) {
      diff = 0;
    }

    this.setState({
      width: this.calcWidth() - diff
    });
  }

  onDragEnd = e => {
    const { newIndex, oldIndex } = e;
    const { resizing } = this.state;

    // work around
    // sortablejs与re-resizable事件冲突, re-resizable无法正常触发onResizeEnd
    if (~resizing) {
      const resizable = this.resizableRefs[resizing];
      const { width } = resizable.state;

      this.onResizeEnd(width, resizing);
      setTimeout(() => resizable.setState({ isResizing: false }));
    }
    // reset width
    this.setState({ 
      width: this.calcWidth()
    });

    if (newIndex === oldIndex) return;

    const changes = { newIndex, oldIndex };
    const { coms, widths } = this.props.data;
    const newComs = utils.sort(coms, changes);
    const newWidths = utils.sort(widths, changes);

    this.props.onPropsChange({
      coms: newComs,
      widths: newWidths
    });
  }

  scrollTo(index) {
    const container = this.containerRef.current;

    if (!container) return;
    
    const $fixed = container.querySelector('.wea-edc-datatable-fixed-left');
    const fixedW = $fixed ? $fixed.offsetWidth : 0;
    const item = container.querySelector('.wea-edc-datatable-item');
    const { offsetLeft: left, offsetWidth: width } = item.children[index];
    const { offsetWidth, scrollLeft } = container;
    let sl = -1;

    // 右侧
    if (left + width > offsetWidth + scrollLeft) {
      sl = left + width - offsetWidth + fixedW;
    } else if (left < scrollLeft) { // 左侧
      sl = left;
    }

    if (~sl) {
      container.scrollLeft = sl;  
    }
  }

  onResizeStart(index) {
    this.setState({ resizing: index });
  }

  onResizeEnd(newWidth, index) {
    const threshold = 0.02;
    const { widths } = this.props.data;
    const radito = newWidth / this.state.width;
    let sum = 0;

    widths[index] = radito;
    sum = utils.reduce(widths);
    
    if (_.inRange(sum, 1 - threshold, 1) 
      || _.inRange(sum, 1, 1 + threshold)
    ) {
      widths[index] = radito + 1 - sum;
    }

    this.props.onPropsChange('widths', widths);
    this.setState({ resizing: -1 });
  }

  onAction(index, actionName) {
    const { data: { coms, widths }, onPropsChange } = this.props;
    const { Delete, Add, Copy } = ActionGroup.types;
    let newComs, newWidths;
    
    switch(actionName) {
      case Delete:
        const com = coms[index];

        if (com) {
          EE.emit(actions.Delete, coms[index]);
        } else {

          newComs = toJS(coms);
          newWidths = toJS(widths);
          
          newComs.splice(index, 1);
          newWidths.splice(index, 1);
          
          newWidths = smoothWidths.forReduce(newWidths);

          onPropsChange({
            coms: newComs,
            widths: newWidths
          });
        }
        break;
      case Copy:
        const uuid = utils.UUID();
        const map = item => ({ ...item, uuid, fieldId: '' });

        newComs = toJS(coms);
        newWidths = toJS(widths);

        utils.copyIndex(newComs, index, map);
        utils.copyIndex(newWidths, index);

        onPropsChange({
          coms: newComs,
          widths: newWidths
        });
        EE.emit(actions.Select, uuid);
        break;
      case Add: 
        let widthsArr = toJS(widths);
        let comsArr = toJS(coms);

        if (comsArr.length <= 0) {
          widthsArr = [1];
          comsArr = [""];
        }

        newComs = utils.addItem(comsArr, '', index + 1);
        newWidths = smoothWidths.forAdd(widthsArr, index + 1, false);

        onPropsChange({
          coms: newComs,
          widths: newWidths
        });
        setTimeout(() => this.scrollTo(index + 1), 10); 
    }
  }

  renderResizableItem(com, style, index, length) {
    const { renderItem } = this.props;

    return (
      <div className='wea-edc-datatable-col'>
        <Resizable ecId={`${this && this.props && this.props.ecId || ''}_Resizable@bkpy2a`}
          size={style}
          enable={{ right: true }}
          minWidth={resizableMinWidth}
          ref={r => this.resizableRefs[index] = r}
          handleWrapperClass='resizable-handle-wrapper'
          onResizeStart={this.onResizeStart.bind(this, index)}
        >
          {renderItem(com)}
        </Resizable>
        <ActionGroup ecId={`${this && this.props && this.props.ecId || ''}_ActionGroup@889q55`}
          onAction={this.onAction.bind(this, index)}
          isDelete={!!(com || length > 1)}
          isCopy={Boolean(com)}
          />   
      </div>
    );
  }

  renderContent(data) {
    const { field } = this.props;
    const { resizing, width } = this.state;
    const coms = data.coms.length > 0 ? data.coms : [''];
    const length = coms.length;
    const widths = parseWidths(toJS(data.widths), width);
    const totalWidth = utils.reduce(toJS(widths)) || 1;
    const dragKey = `${DragKey.DataTable}_${field.uuid}`;
    const contentClsName = classNames('wea-edc-datatable-content', { 
      resizing: resizing > -1,
      covered: totalWidth >= 1,
    });

    return (
      <div ref={this.containerRef} className='wea-edc-datatable-wrapper'>
        <div className={contentClsName}>
          {!data.coms.filter(Boolean).length ? null : <FixedCol ecId={`${this && this.props && this.props.ecId || ''}_FixedCol@g8m51x`} />}
          <Sortable ecId={`${this && this.props && this.props.ecId || ''}_Sortable@f343iu`}
            onChange={() => { }}
            className='wea-edc-datatable-item'
            options={{
              ...sortableOpts,
              group: {
                ...sortableOpts.group,
                pull: [dragKey],
                put: [dragKey]
              },
              onEnd: this.onDragEnd,
              onStart: this.onDragStart.bind(this)
            }}
          >
            {coms.map((com, index) => {
              const style = { width: widths[index] };

              if (!com) {
                return (
                 <div className='wea-edc-datatable-col'>
                    <Droppable ecId={`${this && this.props && this.props.ecId || ''}_Droppable@jcvkxk@${field.uuid}`}
                      style={style}
                      index={index}
                      uuid={field.uuid}
                      options={droppableOpts}
                    />
                    <ActionGroup ecId={`${this && this.props && this.props.ecId || ''}_ActionGroup@8ur4wo@${index}`}
                      onAction={this.onAction.bind(this, index)}
                      isDelete={Boolean(length > 1)}
                      isCopy={Boolean(com)}
                      />  
                  </div>
                );
              }

              return this.renderResizableItem(com, style, index, length);
            })}
          </Sortable>
          </div>
      </div>
    );
  }
}

DataTableDesign.defaultProps = {
  ...BaseDesign.defaultProps,
  data: defaultData,
};